/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.isfd.domain.IsfdLoadExperiment;
import com.comac.ins.isfd.domain.bo.IsfdLoadExperimentBo;
import com.comac.ins.isfd.domain.bo.query.IsfdLoadExperimentQueryBo;
import com.comac.ins.isfd.domain.vo.GridFsTreeNodeVo;
import com.comac.ins.isfd.domain.vo.IsfdLoadExperimentVo;
import com.comac.ins.isfd.mapper.IsfdLoadExperimentMapper;
import com.comac.ins.isfd.service.IGridFsService;
import com.comac.ins.isfd.service.IIsfdLoadExperimentService;
import com.mongodb.client.gridfs.model.GridFSFile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


@Slf4j
@RequiredArgsConstructor
@Service
public class IsfdLoadExperimentServiceImpl implements IIsfdLoadExperimentService {

    @Autowired
    private IsfdLoadExperimentMapper baseMapper;

    @Autowired
    private IGridFsService gridFsService;

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Override
    public IsfdLoadExperimentVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }


    @Override
    public TableDataInfo<IsfdLoadExperimentVo> queryPageList(IsfdLoadExperimentQueryBo queryBo) {
        Integer pageNum = queryBo.getPageNum();
        Integer pageSize = queryBo.getPageSize();
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(pageNum);
        pageQuery.setPageSize(pageSize);
        LambdaQueryWrapper<IsfdLoadExperiment> lqw = buildQueryWrapper(queryBo);
        Page<IsfdLoadExperimentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public List<IsfdLoadExperimentVo> queryList(IsfdLoadExperimentBo bo) {
        LambdaQueryWrapper<IsfdLoadExperiment> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<IsfdLoadExperiment> buildQueryWrapper(IsfdLoadExperimentQueryBo bo) {
        LambdaQueryWrapper<IsfdLoadExperiment> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getLoadcaseVersionId() != null, IsfdLoadExperiment::getLoadcaseVersionId, bo.getLoadcaseVersionId());
        lqw.like(StringUtils.isNotBlank(bo.getLoadExperimentName()), IsfdLoadExperiment::getLoadExperimentName, bo.getLoadExperimentName());
        lqw.eq(StringUtils.isNotBlank(bo.getLoadType()), IsfdLoadExperiment::getLoadType, bo.getLoadType());
        lqw.eq(StringUtils.isNotBlank(bo.getStage()), IsfdLoadExperiment::getStage, bo.getStage());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalFileName()), IsfdLoadExperiment::getOriginalFileName, bo.getOriginalFileName());
        lqw.like(StringUtils.isNotBlank(bo.getNodeInformationFileName()), IsfdLoadExperiment::getNodeInformationFileName, bo.getNodeInformationFileName());
        lqw.like(StringUtils.isNotBlank(bo.getNodeLoadNumberFileName()), IsfdLoadExperiment::getNodeLoadNumberFileName, bo.getNodeLoadNumberFileName());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorHingeCoordinateSystemInformation()), IsfdLoadExperiment::getElevatorHingeCoordinateSystemInformation, bo.getElevatorHingeCoordinateSystemInformation());
        lqw.eq(StringUtils.isNotBlank(bo.getState()), IsfdLoadExperiment::getState, bo.getState());
        lqw.eq(bo.getHistoryLoadExperimentId() != null, IsfdLoadExperiment::getHistoryLoadExperimentId, bo.getHistoryLoadExperimentId());
        lqw.eq(StringUtils.isNotBlank(bo.getDirectionalRudderCoordinateSystemInformation()), IsfdLoadExperiment::getDirectionalRudderCoordinateSystemInformation, bo.getDirectionalRudderCoordinateSystemInformation());
        lqw.eq(StringUtils.isNotBlank(bo.getCumulativeResult1g()), IsfdLoadExperiment::getCumulativeResult1g, bo.getCumulativeResult1g());
        lqw.eq(StringUtils.isNotBlank(bo.getUploadCustomizationFile()), IsfdLoadExperiment::getUploadCustomizationFile, bo.getUploadCustomizationFile());
        lqw.eq(StringUtils.isNotBlank(bo.getRoundNumber()), IsfdLoadExperiment::getRoundNumber, bo.getRoundNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getSubType()), IsfdLoadExperiment::getSubType, bo.getSubType());
        lqw.like(StringUtils.isNotBlank(bo.getLoadName()), IsfdLoadExperiment::getLoadName, bo.getLoadName());
        lqw.eq(StringUtils.isNotBlank(bo.getModelNumber()), IsfdLoadExperiment::getModelNumber, bo.getModelNumber());
        return lqw;
    }

    private LambdaQueryWrapper<IsfdLoadExperiment> buildQueryWrapper(IsfdLoadExperimentBo bo) {
        LambdaQueryWrapper<IsfdLoadExperiment> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getLoadcaseVersionId() != null, IsfdLoadExperiment::getLoadcaseVersionId, bo.getLoadcaseVersionId());
        lqw.like(StringUtils.isNotBlank(bo.getLoadExperimentName()), IsfdLoadExperiment::getLoadExperimentName, bo.getLoadExperimentName());
        lqw.eq(StringUtils.isNotBlank(bo.getLoadType()), IsfdLoadExperiment::getLoadType, bo.getLoadType());
        lqw.eq(StringUtils.isNotBlank(bo.getStage()), IsfdLoadExperiment::getStage, bo.getStage());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalFileName()), IsfdLoadExperiment::getOriginalFileName, bo.getOriginalFileName());
        lqw.like(StringUtils.isNotBlank(bo.getNodeInformationFileName()), IsfdLoadExperiment::getNodeInformationFileName, bo.getNodeInformationFileName());
        lqw.like(StringUtils.isNotBlank(bo.getNodeLoadNumberFileName()), IsfdLoadExperiment::getNodeLoadNumberFileName, bo.getNodeLoadNumberFileName());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorHingeCoordinateSystemInformation()), IsfdLoadExperiment::getElevatorHingeCoordinateSystemInformation, bo.getElevatorHingeCoordinateSystemInformation());
        lqw.eq(StringUtils.isNotBlank(bo.getState()), IsfdLoadExperiment::getState, bo.getState());
        lqw.eq(bo.getHistoryLoadExperimentId() != null, IsfdLoadExperiment::getHistoryLoadExperimentId, bo.getHistoryLoadExperimentId());
        lqw.eq(StringUtils.isNotBlank(bo.getDirectionalRudderCoordinateSystemInformation()), IsfdLoadExperiment::getDirectionalRudderCoordinateSystemInformation, bo.getDirectionalRudderCoordinateSystemInformation());
        lqw.eq(StringUtils.isNotBlank(bo.getCumulativeResult1g()), IsfdLoadExperiment::getCumulativeResult1g, bo.getCumulativeResult1g());
        lqw.eq(StringUtils.isNotBlank(bo.getUploadCustomizationFile()), IsfdLoadExperiment::getUploadCustomizationFile, bo.getUploadCustomizationFile());
        lqw.eq(StringUtils.isNotBlank(bo.getRoundNumber()), IsfdLoadExperiment::getRoundNumber, bo.getRoundNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getSubType()), IsfdLoadExperiment::getSubType, bo.getSubType());
        lqw.like(StringUtils.isNotBlank(bo.getLoadName()), IsfdLoadExperiment::getLoadName, bo.getLoadName());
        lqw.eq(StringUtils.isNotBlank(bo.getModelNumber()), IsfdLoadExperiment::getModelNumber, bo.getModelNumber());
        return lqw;
    }


    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public Boolean save(IsfdLoadExperimentBo bo) {
        IsfdLoadExperiment po = MapstructUtils.convert(bo, IsfdLoadExperiment.class);
        baseMapper.insert(po);
        return Boolean.TRUE;
    }

    @Override
    public GridFsTreeNodeVo queryFileList(Long loadExperimentId, String fileName) {
        return gridFsService.buildFolderTreeByVersionId(String.valueOf(loadExperimentId), fileName);
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadFiles(List<String> ids) {
        if (ids.size() == 1) {
            try {
                GridFsResource resource = gridFsService.downloadFileById(ids.get(0));
                String filename = URLEncoder.encode(resource.getFilename(), StandardCharsets.UTF_8);
                return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .contentLength(resource.contentLength())
                    .body(new InputStreamResource(resource.getInputStream()));
            } catch (IOException e) {
                log.error("Failed to download file", e);
                throw new BaseException("Failed to download file with id: " + ids.get(0));
            }
        } else {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            try (ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream)) {
                for (String id : ids) {
                    GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)));
                    if (gridFSFile == null) {
                        throw new FileNotFoundException("File not found with id: " + id);
                    }
                    Document metadata = gridFSFile.getMetadata();
                    String folder = metadata != null ? metadata.getString("folder") : null;
                    String entryName = (folder == null || folder.isEmpty())
                        ? gridFSFile.getFilename()
                        : folder + File.separator + gridFSFile.getFilename();

                    zipOut.putNextEntry(new ZipEntry(entryName));
                    try (InputStream inputStream = gridFsTemplate.getResource(gridFSFile).getInputStream()) {
                        IOUtils.copy(inputStream, zipOut);
                    } finally {
                        zipOut.closeEntry();
                    }
                }
            } catch (IOException e) {
                log.error("Failed to download file", e);
                throw new BaseException("Failed to generate ZIP file");
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", "files.zip");
            return ResponseEntity.ok()
                .headers(headers)
                .body(new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())));
        }
    }
}
